#ifndef CSERVER_COMMON_IO_S11N_BASE_H__
#define CSERVER_COMMON_IO_S11N_BASE_H__

#include <type_traits>
#include <cserver/range.h>

namespace csrv{ 
	namespace private_{
		template<typename ...T> struct total_size_of;
		template<> struct total_size_of<> {
			static const size_t value = 0;
		};

		template<typename T, typename ... U>
			struct total_size_of<T, U...> {
				static_assert(std::is_scalar<T>::value, "don't known how to skip a non-pod type (due to padding issue).");
				static const size_t value = sizeof(T) + total_size_of<U...>::value;
			};
	}
	template<size_t N> struct skip_n { };
	template<typename ... T> struct skip_scalar : skip_n<private_::total_size_of<T...>::value> { };
	template<typename T> struct skip_t : skip_n<sizeof(T)>{ 
		typedef T type;
	};
	template<int> struct dummy{ dummy(int){}};


	template<typename Ar>
		range<char*> block_read(Ar& rd, const range<char*>& buf)
		{
			auto reset = buf;
			while(!reset.empty() && rd.readable())
				reset = rd.read(reset);
			return reset;
		}
	template<typename Ar>
		range<const char*> block_write(Ar& wr, const range<const char*>& buf){
			auto reset = buf;
			while(!reset.empty() && wr.writable())
				reset = wr.write(reset);
			return reset;
		}

	namespace s11n{
		struct serializer_tag{};
		struct deserializer_tag{};
		struct context_serializer_tag : serializer_tag{};
		struct context_deserializer_tag : deserializer_tag{};

		template <typename T>
			typename T::tag_type get_tag_type_fun_(T* t);

		// Used as fallback when SFINAE culls the template method
		void get_tag_type_fun_(...);

		template<typename T>
			struct get_tag_type{
				typedef decltype(get_tag_type_fun_((T*)0)) type;
			};

		template<typename Ar>
			struct is_serializer : public std::is_convertible<typename get_tag_type<Ar>::type, serializer_tag>{ };
		template<typename Ar>
			struct is_deserializer : public std::is_convertible<typename get_tag_type<Ar>::type, deserializer_tag>{ };

		template<typename Ar>
			struct is_context_serializer : public std::is_convertible<typename get_tag_type<Ar>::type, context_serializer_tag>{ };
		template<typename Ar>
			struct is_context_deserializer : public std::is_convertible<typename get_tag_type<Ar>::type, context_deserializer_tag>{ };

		template<typename Ar>
			struct serializer_base {
				typedef serializer_tag  tag_type;

				explicit serializer_base(Ar& ar) : m_archive(&ar){};
				operator Ar& () const {
					return get();
				}
				Ar& get() const{ return *m_archive;}
				private:
				Ar* m_archive;
			};

		template<typename Ar>
			struct deserializer_base {
				typedef deserializer_tag  tag_type;

				explicit deserializer_base(Ar& ar) : m_archive(&ar){};
				operator Ar& () const {
					return get();
				}
				Ar& get() const{ return *m_archive;}
				private:
				Ar* m_archive;
			};

		template<typename Ar, typename ContextType>
			struct context_serializer : public Ar{
				static_assert(is_serializer<Ar>::value, "context_serializer require a serializer");
				typedef context_serializer  tag_type;
				context_serializer(Ar& ar, ContextType& c) : context(&c){}
				ContextType* context;
			};
		template<typename Ar, typename ContextType>
			context_serializer<Ar, ContextType> make_context_serializer(Ar& ar, ContextType& c){
				return context_serializer<Ar, ContextType>(ar, c);
			}
		template<typename Ar, typename ContextType>
			struct context_deserializer : public Ar{
				static_assert(is_deserializer<Ar>::value, "context_deserializer require a deserializer");
				typedef context_deserializer  tag_type;
				context_deserializer(Ar& ar, ContextType& c) : context(&c){}
				ContextType* context;
			};
		template<typename Ar, typename ContextType>
			context_deserializer<Ar, ContextType> make_context_deserializer(Ar& ar, ContextType& c){
				return context_deserializer<Ar, ContextType>(ar, c);
			}


		template<typename Ar> struct serializer : public serializer_base<Ar>{
			explicit serializer(Ar& ar) : serializer_base<Ar>(ar){};
		};
		template<typename Ar> struct deserializer : public deserializer_base<Ar>{
			explicit deserializer(Ar& ar) : deserializer_base<Ar>(ar){};
		};

		template<typename Ar> serializer<Ar> as_sink(Ar& ar){ return serializer<Ar>(ar);}
		template<typename Ar> deserializer<Ar> as_source(Ar& ar){ return deserializer<Ar>(ar);}

		template<typename Ar, typename T,
			typename = typename std::enable_if<std::is_scalar<T>::value &&
				is_serializer<Ar>::value>::type>
				Ar& save(Ar& wt, const T& t)
				{
					auto first = reinterpret_cast<const char*>(&t);
					auto last = reinterpret_cast<const char*>(&t + 1);

					block_write(wt.get(), make_range(first, last));

					return wt;
				}

		template<typename Ar, typename T,
			typename = typename std::enable_if<std::is_scalar<T>::value &&
				is_deserializer<Ar>::value>::type>
				Ar& load(Ar& rd, T& t)
				{
					auto first = reinterpret_cast<char*>(&t);
					auto last = reinterpret_cast<char*>(&t + 1);

					if (!block_read(rd.get(), make_range(first, last)).empty() )
						throw std::runtime_error("no enough data");

					return rd;
				}

		template<typename Ar, typename T,
			typename = typename std::enable_if<std::is_empty<T>::value &&
				is_serializer<Ar>::value>::type>
				Ar& save(Ar& wt, const T&, dummy<0> = 0)
				{
					return wt;
				}

		template<typename Ar, typename T,
			typename = typename std::enable_if<std::is_empty<T>::value &&
				is_deserializer<Ar>::value>::type>
				Ar& load(Ar& rd, T&, dummy<0> = 0)
				{
					return rd;
				}

	}
	template<typename Ar, typename T,
		typename = typename std::enable_if<s11n::is_deserializer<Ar>::value, Ar>::type>
			Ar& operator&(Ar& ar, T& t){	//load
				return load(ar, t);
			}

	template<typename Ar, size_t N,
		typename = typename std::enable_if<s11n::is_deserializer<Ar>::value, Ar>::type>
			Ar& operator&(Ar& ar, skip_n<N> t){       //load
				return load(ar, t);
			}


	template<typename Ar, typename T,
		typename = typename std::enable_if<s11n::is_serializer<Ar>::value, Ar>::type>
			Ar& operator&(Ar& ar, const T& t){	//save
				return save(ar, t);
			}

	template<typename Ar, size_t N,
		typename = typename std::enable_if<s11n::is_serializer<Ar>::value>::type>
			Ar& save(Ar& wt, skip_n<N> t)
			{
				wt.seek(wt.offset() + N);
				return wt;
			}

	template<typename Ar, size_t N,
		typename = typename std::enable_if<s11n::is_deserializer<Ar>::value>::type>
			Ar& load(Ar& rd, skip_n<N>)
			{
				rd.seek(rd.offset() + N);
				return rd;
			}

	template<typename T, typename Ar,
		typename = typename std::enable_if<s11n::is_deserializer<Ar>::value>::type>
			T load(Ar& rd)
			{
				T t;
				load(rd, t);
				return std::move(t);
			}

}
#endif //end CSERVER_COMMON_IO_S11N_BASE_H__

